home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue28 / subclass / AutoMemo.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-09-07  |  7.6 KB  |  263 lines

  1. unit AutoMemo;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  7.   StdCtrls;
  8.  
  9. type
  10.   eAutoMemoError = class(exception);
  11.  
  12.   tSynMemoScrollEnum = (smAutoScroll, smVertical, smHoriz, smBoth, smNoScroll);
  13.  
  14.   TAutoMemo = class(TCustomMemo)
  15.   private
  16.     fScrolling : tSynMemoScrollEnum;
  17.     oldwinproc : pointer;
  18.     newWinProc : pointer;
  19.     fSubClassDone : boolean;
  20.     procedure SubClassParentFunc(var Msg : tmessage);
  21.     function getScrollOption : tsynMemoScrollEnum;
  22.     procedure setScrollOption(aOption : tSynMemoScrollEnum);
  23.     function getLinesShowing : integer;
  24.     procedure setLinesShowing(numLines : integer);
  25.     function getLineHeight : integer;
  26.     function getLineWidth(aline: integer): integer;
  27.     procedure WMERASEBKGND(var Message: TMessage); message WM_ERASEBKGND;
  28.   protected
  29.     procedure CheckScrolling; virtual;
  30.     procedure TurnOnSubClassing;
  31.     Procedure TurnOffSubClassing;
  32.     function LongestLine : integer;
  33.   public
  34.     constructor create(aOwner : tcomponent); override;
  35.     destructor destroy; override;
  36.     property LineHeight : integer read GetLineHeight;
  37.     property LineWidth[aline : integer] : integer read GetLineWidth;
  38.   published
  39.     property ScrollOption : tsynMemoScrollEnum read getSCrollOption write setScrollOption default smAutoScroll;
  40.     property Align;
  41.     property Alignment;
  42.     property BorderStyle;
  43.     property Color;
  44.     property Ctl3D;
  45.     property DragCursor;
  46.     property DragMode;
  47.     property Enabled;
  48.     property Font;
  49.     property HideSelection;
  50.     property Lines;
  51.     property MaxLength;
  52.     property OEMConvert;
  53.     property ParentColor;
  54.     property ParentCtl3D;
  55.     property ParentFont;
  56.     property ParentShowHint;
  57.     property PopupMenu;
  58.     property ReadOnly;
  59. //    property ScrollBars;
  60.     property ShowHint;
  61.     property TabOrder;
  62.     property TabStop;
  63.     property Visible;
  64.     property WantReturns;
  65.     property WantTabs;
  66.     property WordWrap;
  67.     property OnChange;
  68.     property OnClick;
  69.     property OnDblClick;
  70.     property OnDragDrop;
  71.     property OnDragOver;
  72.     property OnEndDrag;
  73.     property OnEnter;
  74.     property OnExit;
  75.     property OnKeyDown;
  76.     property OnKeyPress;
  77.     property OnKeyUp;
  78.     property OnMouseDown;
  79.     property OnMouseMove;
  80.     property OnMouseUp;
  81.   end;
  82.  
  83. procedure Register;
  84.  
  85. implementation
  86.  
  87. function maxOf(const first, second : integer):integer;
  88. begin
  89.   if first > second
  90.     then result := first
  91.     else result := second;
  92. end;
  93.  
  94.  
  95. constructor tAutoMemo.create(aOwner : tcomponent);
  96. begin
  97.   inherited create(aOwner);
  98.   fScrolling := smAutoScroll;
  99.   fsubClassDone := false;
  100. end;
  101.  
  102. Destructor tAutoMemo.destroy;
  103. begin
  104.   FreeObjectInstance(NewWinProc);
  105.   inherited destroy;
  106. end;
  107.  
  108. procedure tAutoMemo.WMERASEBKGND(var Message: TMessage);
  109. begin
  110.   inherited;
  111.   if not fSubclassDone
  112.     then TurnOnSubclassing;
  113. end;
  114.  
  115. Procedure tAutoMemo.TurnOnSubclassing;
  116. begin
  117.   if HandleAllocated and (not fsubclassDone)
  118.        then begin
  119.          if NewWinProc <> nil
  120.            then FreeObjectInstance(newWinProc);
  121.          NewWinProc := MakeObjectInstance(SubClassParentFunc);
  122.          OldWinProc := Pointer(setWindowLong(parent.handle, GWL_WNDPROC, longint(NewWinProc)));
  123.          if OldWinProc = nil
  124.            then raise eAutoMemoError.create('subclass failed');
  125.          fSubClassDone := true;
  126.          end;
  127. end;
  128.  
  129. Procedure tAutoMemo.TurnOffSubclassing;
  130. begin
  131.   SetWindowLong(parent.handle, GWL_WNDPROC, longint(OldWinProc));
  132.   fSubclassDone := false;
  133. end;
  134.  
  135. function tAutoMemo.getScrollOption : tsynMemoScrollEnum;
  136. begin result := fScrolling end;
  137.  
  138. procedure tAutoMemo.setScrollOption(aOption : tSynMemoScrollEnum);
  139. begin
  140.   fSCrolling := aOption;
  141.   CheckScrolling;
  142. end;
  143.  
  144. Procedure tAutoMemo.CheckScrolling;
  145. var tmpScroll : tScrollStyle;
  146.  
  147.   function Translate(customScrollEnum : tSynMemoScrollEnum ): tscrollstyle;
  148.   begin
  149.     case CustomScrollEnum of
  150.       smAutoScroll : result := ssNone;
  151.       smVertical   : result := ssVertical;
  152.       smHoriz      : result := ssHorizontal;
  153.       smBoth       : result := ssBoth;
  154.       smNoScroll   : result := ssNone;
  155.       end;
  156.     end;
  157.  
  158. begin
  159.   tmpScroll := translate(fScrolling);
  160.   if fScrolling = smAutoScroll
  161.     then begin
  162.       if not fSubClassDone
  163.         then TurnOnSubClassing;
  164.       if lines.count*lineheight > clientheight
  165.            then tmpScroll := ssVertical
  166.            else tmpScroll := ssNone;
  167.        if WordWrap = false
  168.          then if (longestline > clientwidth) and (getLinesShowing > 1)
  169.              then begin
  170.                 if TmpScroll = ssVertical
  171.                    then TmpScroll := ssBoth
  172.                    else TmpScroll := ssHorizontal;
  173.                 end
  174.              else begin
  175.                 if TmpScroll = ssBoth
  176.                    then TmpScroll := ssVertical
  177.                    else if lines.count*lineheight > clientheight
  178.                      then TmpScroll := ssVertical
  179.                      else TmpScroll := ssNone;
  180.                 end;
  181.       end
  182.     else TurnOffSubClassing;
  183.   case fSCrolling of
  184.     smAutoSCroll : if scrollbars <> TmpScroll then scrollbars := tmpScroll;
  185.     smVertical : if scrollbars <> ssVertical then scrollbars := ssVertical;
  186.     smHoriz : if scrollbars <> ssHorizontal then scrollbars := ssHorizontal;
  187.     smBoth  : if scrollbars <> ssBoth then scrollbars := ssboth;
  188.     smNoScroll : if scrollbars <> ssNone then scrollbars := ssnone;
  189.     end;
  190. end;
  191.  
  192. procedure tAutoMemo.SubClassParentFunc(var Msg : tmessage);
  193. begin
  194.   with msg do begin
  195.     Result := CallWindowProc(OldWinProc, Parent.handle, Msg, wParam, lParam);
  196.     if (msg = WM_COMMAND) and (lparam = handle)
  197.       then if WParamHi = en_change then CheckSCrolling;
  198.     if msg = WM_DESTROY
  199.       then   SetWindowLong(parent.handle, GWL_WNDPROC, longint(OldWinProc));
  200.     end;
  201. end;
  202.  
  203.  
  204. function tAutoMemo.getLineHeight : integer;
  205. Var
  206.   oldfont: HFont;  {the old font}
  207.   dc: THandle;     {a dc handle}
  208.   tm: TTextMetric; {text metric structure}
  209.   textSize : tSize;
  210. const junk : pchar = 'X';
  211. begin
  212.   result := (height - clientheight);
  213.   dc := GetDC(handle); {Get the Dc for the memo}
  214.   oldFont := SelectObject(dc, Font.handle); {now make sure we have the memo's font}
  215.     {if I don't do the line above, then the text size is 2 pixels too big...}
  216.   GetTextMetrics(dc, tm); {Get the text metric info}
  217.   GetTextExtentPoint32(dc, junk, 1, textSize); {and get the height in this font}
  218.   result := textsize.cy + 2*tm.tmExternalLeading;
  219.   SelectObject(dc, oldfont); {Select the old font -- I'm not sure if or why we need this, but Lloyd's file said so...}
  220.   ReleaseDC(handle, dc); {Release the Dc}
  221. end;
  222.  
  223. function tAutoMemo.getLineWidth(aline: integer): integer;
  224. var
  225.   oldfont: HFont;  {the old font}
  226.   dc: THandle;     {a dc handle}
  227.   textSize : tSize;
  228. begin
  229.    dc := GetDC(handle);
  230.   oldFont := SelectObject(dc, Font.handle); {Select the memo's font}
  231.    GetTextExtentPoint32(dc, pchar(lines[aline]), length(lines[aline]), textSize);
  232.    result := textsize.cx;
  233.   SelectObject(dc, oldfont); {Select the old font}
  234.   ReleaseDC(handle, dc); {Release the Dc}
  235. end;
  236.  
  237.  
  238. function tAutoMemo.LongestLine: integer;
  239. var i : integer;
  240. begin
  241.   result := 0;
  242.   for i := 0 to lines.count - 1 do
  243.     result := maxof(result, LineWidth[i]);
  244. end;
  245.  
  246. function tAutoMemo.getLinesShowing : integer;
  247. begin
  248.   result := (Height - (height - clientHeight+2)) div lineheight;
  249. end;
  250.  
  251. procedure tAutoMemo.setLinesShowing(numLines : integer);
  252. begin
  253.   height := (maxof(numlines,0))*lineheight + (Height - ClientHeight+2);
  254. end;
  255.  
  256.  
  257. procedure Register;
  258. begin
  259.   RegisterComponents('Samples', [TAutoMemo]);
  260. end;
  261.  
  262. end.
  263.